library(tidyverse)
library(interactions)  # for probe_interactions() plot

salary <- read_csv("../../data/salary_lec.csv", show_col_types = FALSE) |>
  mutate(
    envt = factor(ifelse(dept == 1, 'nature', 'urban'))
  )

# make the acct slope a bit steeper to illustrate the point better
outdoors <- salary |>
  mutate(
    salary_aug = ifelse(
      envt == 'nature',
      salary + (service^2)/1.5 - 10,
      salary
    ),
    wellbeing = salary_aug
  ) |>
  rename(
    outdoor_time = service
  ) |>
  select(wellbeing, envt, outdoor_time)

Plot data

palette_probe <- c('#4ab8fc', '#ff7b01')

outdoors |>
  ggplot(aes(x = outdoor_time, y = wellbeing, colour = envt)) +
  geom_point(size = 5) +
  geom_smooth(method = 'lm', se = F) +
  # facet_wrap(~ envt) +
  # theme(legend.position = 'none') +
  scale_color_manual(values = palette_probe) +
  NULL

envt:

contrasts(outdoors$envt)
       urban
nature     0
urban      1

wellbeing:

outdoor_time:

Model: no interaction

m1 <- lm(wellbeing ~ outdoor_time + envt, data = outdoors)
summary(m1)

Call:
lm(formula = wellbeing ~ outdoor_time + envt, data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-15.1784  -4.1564  -0.6475   3.4551  20.1555 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   19.0465     4.9210   3.870 0.000334 ***
outdoor_time   7.7724     0.9493   8.188 1.34e-10 ***
envturban    -26.2466     1.9600 -13.391  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.895 on 47 degrees of freedom
Multiple R-squared:  0.8511,    Adjusted R-squared:  0.8447 
F-statistic: 134.3 on 2 and 47 DF,  p-value: < 2.2e-16
outdoors_probedat <- outdoors |>
  mutate(
    pred = outdoor_time,
    modx_group = envt
  )
  
plot_m1_probe <- probe_interaction(
  model = m1,
  pred = outdoor_time,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 5)

plot_data_probe <- plot_m1_probe

plot_data_probe$layers[[1]] <- NULL
plot_data_probe$layers[[1]] <- NULL
plot_m1_probe

meeting in the middle means that neither line fits the data very well

plot raw data

plot_data_probe

Compute simple slopes

coef(m1)
 (Intercept) outdoor_time    envturban 
   19.046503     7.772417   -26.246592 

Compute simple slopes.

\[ \begin{align} \widehat{wellbeing} &= \beta_0 + (\beta_1 \cdot outdoor_time) + (\beta_2 \cdot envt) \\ \widehat{wellbeing} &= 19 + (7.8 \cdot outdoor_time) + (-26.2 \cdot envt) \\ \end{align} \]

When \(envt = 0\) (nature):

\[ \begin{align} \widehat{wellbeing}_{envt=0} &= 19 + (7.8 \cdot outdoor_time) + (-26.2 \cdot 0) \\ \widehat{wellbeing}_{envt=0} &= 19 + (7.8 \cdot outdoor_time) \\ \end{align} \] When \(envt = 1\) (urban):

\[ \begin{align} \widehat{wellbeing}_{envt=1} &= 19 + (7.8 \cdot outdoor_time) + (-26.2 \cdot 1) \\ \widehat{wellbeing}_{envt=1} &= 19 - 26.2 + (7.8 \cdot outdoor_time) \\ \widehat{wellbeing}_{envt=1} &= 9 + (7.8 \cdot outdoor_time) \\ \end{align} \]

Model: with interaction

m2 <- lm(wellbeing ~ outdoor_time + envt + outdoor_time:envt, data = outdoors)
summary(m2)

Call:
lm(formula = wellbeing ~ outdoor_time + envt + outdoor_time:envt, 
    data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.0029  -2.9519  -0.4881   3.1881  11.2969 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)             -3.3876     4.5651  -0.742  0.46183    
outdoor_time            12.2887     0.8982  13.682  < 2e-16 ***
envturban               20.2899     6.5022   3.120  0.00312 ** 
outdoor_time:envturban  -9.5597     1.3067  -7.316 3.07e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.738 on 46 degrees of freedom
Multiple R-squared:  0.9312,    Adjusted R-squared:  0.9267 
F-statistic: 207.4 on 3 and 46 DF,  p-value: < 2.2e-16

Compute simple slopes

coef(m2)
           (Intercept)           outdoor_time              envturban outdoor_time:envturban 
             -3.387579              12.288744              20.289905              -9.559714 

By hand

\[ \begin{align} \widehat{wellbeing} &= \beta_0 + (\beta_1 \cdot out) + (\beta_2 \cdot envt) + (\beta_3 \cdot out \cdot envt)\\ \widehat{wellbeing} &= -3.4 + (12.3 \cdot out) + (20.3 \cdot envt) + (-9.6 \cdot out \cdot envt)\\ \end{align} \]

When \(envt = 0\) (nature):

\[ \begin{align} \widehat{wellbeing}_{acct} &= -3.4 + (12.3 \cdot out) + (20.3 \cdot envt) + (-9.6 \cdot out \cdot envt)\\ \widehat{wellbeing}_{acct} &= -3.4 + (12.3 \cdot out) + (20.3 \cdot 0) + (-9.6 \cdot out \cdot 0)\\ \widehat{wellbeing}_{acct} &= -3.4 + (12.3 \cdot out)\\ \end{align} \]

When \(envt = 1\) (urban):

\[ \begin{align} \widehat{wellbeing}_{mng} &= -3.4 + (12.3 \cdot out) + (20.3 \cdot envt) + (-9.6 \cdot out \cdot envt)\\ \widehat{wellbeing}_{mng} &= -3.4 + (12.3 \cdot out) + (20.3 \cdot 1) + (-9.6 \cdot out \cdot 1)\\ \widehat{wellbeing}_{mng} &= -3.4 + 20.3 + (12.3 \cdot out) + (-9.6 \cdot out)\\ \widehat{wellbeing}_{mng} &= 16.9 + (12.3 \cdot out) + (-9.6 \cdot out)\\ \widehat{wellbeing}_{mng} &= 16.9 + ((12.3 - 9.6) \cdot out) \\ \widehat{wellbeing}_{mng} &= 16.9 + (2.7 \cdot out)\\ \end{align} \]

in R

When envt = 0 (nature):

coef(m2)[['(Intercept)']]  # intercept
[1] -3.387579
coef(m2)[['outdoor_time']]      # slope
[1] 12.28874

When envt = 1 (urban)

coef(m2)[['(Intercept)']] + coef(m2)[['envturban']]      # intercept
[1] 16.90233
coef(m2)[['outdoor_time']] + coef(m2)[['outdoor_time:envturban']]  # slope
[1] 2.72903

Plot asis model

plot_m2_probe <- probe_interaction(
  model = m2,
  pred = outdoor_time,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 3) +

  NULL

plot_m2_probe

Asis y-intercept plot

m2_simple_effs <- tibble(
  envt = c('nature', 'urban'),
  modx_group = c('nature', 'urban'),
  int = c(
    coef(m2)[['(Intercept)']], 
    coef(m2)[['(Intercept)']] + coef(m2)[['envturban']]
    ),
  slp = c(
    coef(m2)[['outdoor_time']],
    coef(m2)[['outdoor_time']] + coef(m2)[['outdoor_time:envturban']]
    )
)

plot_m2_probe_intercept <- plot_m2_probe
plot_m2_probe_intercept$layers[[1]] <- NULL

plot_m2_probe_intercept +
  xlim(-7, 7) +
  ylim(-10, 100) +
  geom_abline(
    data = m2_simple_effs, 
    aes(intercept = int, slope = slp, colour = envt, linetype = envt),
    linewidth = 1.5
  ) +
  geom_point(
    data = m2_simple_effs,
    x = 0,
    aes(y = int),
    size = 5,
    shape = 15
  ) +
  geom_vline(xintercept = 0, linetype = 'dotted') +
  labs(
    subtitle = 'outdoor_time as-is'
  ) +
  theme(
    legend.position = 'bottom'
  ) +
  NULL

Min-shift predictor

Plot predictor as-is.

outdoors |>
  ggplot(aes(x = outdoor_time, y = wellbeing)) +
  geom_vline(xintercept = 0, colour = 'red', linewidth = 2) +
  geom_point() +
  NULL

Plot min-shifted predictor.

outdoors <- outdoors |>
  mutate(
    outdoor_time_min = outdoor_time - min(outdoor_time)
  )

outdoors |>
  ggplot(aes(x = outdoor_time_min, y = wellbeing)) +
  geom_vline(xintercept = 0, colour = 'red', linewidth = 2) +
  geom_point() +
  NULL

0 is meaningful now = the minimum length of outdoor_time.

Model

m3 <- lm(wellbeing ~ outdoor_time_min + envt + outdoor_time_min:envt, data = outdoors)
summary(m3)

Call:
lm(formula = wellbeing ~ outdoor_time_min + envt + outdoor_time_min:envt, 
    data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.0029  -2.9519  -0.4881   3.1881  11.2969 

Coefficients:
                           Estimate Std. Error t value Pr(>|t|)    
(Intercept)                 28.6649     2.3291  12.307 3.72e-16 ***
outdoor_time_min            12.2887     0.8982  13.682  < 2e-16 ***
envturban                   -4.6445     3.2455  -1.431    0.159    
outdoor_time_min:envturban  -9.5597     1.3067  -7.316 3.07e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.738 on 46 degrees of freedom
Multiple R-squared:  0.9312,    Adjusted R-squared:  0.9267 
F-statistic: 207.4 on 3 and 46 DF,  p-value: < 2.2e-16

Compute simple slopes

In R

When envt = 0 (acct):

coef(m3)[['(Intercept)']]  # intercept
[1] 28.66493
coef(m3)[['outdoor_time_min']]      # slope
[1] 12.28874

When envt = 1 (mng)

coef(m3)[['(Intercept)']] + coef(m3)[['envturban']]      # intercept
[1] 24.02041
coef(m3)[['outdoor_time_min']] + coef(m3)[['outdoor_time_min:envturban']]  # slope
[1] 2.72903

Plot shifted model

outdoors_probedat <- outdoors_probedat |>
  mutate(
    outdoor_time_min = outdoor_time - min(outdoor_time)
  )

plot_m3_probe <- probe_interaction(
  model = m3,
  pred = outdoor_time_min,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 3)

plot_m3_probe

Shifted y-intercept plot

m3_simple_effs <- tibble(
  envt = c('nature', 'urban'),
  modx_group = c('nature', 'urban'),
  int = c(
    coef(m3)[['(Intercept)']], 
    coef(m3)[['(Intercept)']] + coef(m3)[['envturban']]
    ),
  slp = c(
    coef(m3)[['outdoor_time_min']],
    coef(m3)[['outdoor_time_min']] + coef(m3)[['outdoor_time_min:envturban']]
    )
)

plot_m3_probe_intercept <- plot_m3_probe
plot_m3_probe_intercept$layers[[1]] <- NULL

plot_m3_probe_intercept +
  xlim(-7, 7) +
    ylim(-10, 100) +
  geom_abline(
    data = m3_simple_effs, 
    aes(intercept = int, slope = slp, colour = envt, linetype = envt),
    linewidth = 1.5
  ) +
  geom_point(
    data = m3_simple_effs,
    x = 0,
    aes(y = int),
    size = 5,
    shape = 15
  ) +
  geom_vline(xintercept = 0, linetype = 'dotted') +
  labs(
    subtitle = 'outdoor_time min-shifted'
  ) +
  theme(
    legend.position = 'bottom'
  ) +
  NULL

Centre predictor

Plot centered predictor.

outdoors <- outdoors |>
  mutate(
    outdoor_time_c = scale(outdoor_time, center = TRUE, scale = FALSE)
  )

outdoors |>
  ggplot(aes(x = outdoor_time_c, y = wellbeing)) +
  geom_vline(xintercept = 0, colour = 'red', linewidth = 2) +
  geom_point() +
  NULL

round(mean(outdoors$outdoor_time_c))
[1] 0

Model

m4 <- lm(wellbeing ~ outdoor_time_c + envt + outdoor_time_c:envt, data = outdoors)
summary(m4)

Call:
lm(formula = wellbeing ~ outdoor_time_c + envt + outdoor_time_c:envt, 
    data = outdoors)

Residuals:
     Min       1Q   Median       3Q      Max 
-10.0029  -2.9519  -0.4881   3.1881  11.2969 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)               56.4513     0.9712  58.124  < 2e-16 ***
outdoor_time_c            12.2887     0.8982  13.682  < 2e-16 ***
envturban                -26.2602     1.3469 -19.496  < 2e-16 ***
outdoor_time_c:envturban  -9.5597     1.3067  -7.316 3.07e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4.738 on 46 degrees of freedom
Multiple R-squared:  0.9312,    Adjusted R-squared:  0.9267 
F-statistic: 207.4 on 3 and 46 DF,  p-value: < 2.2e-16

Plot centered model

outdoors_probedat <- outdoors_probedat |>
  mutate(
    outdoor_time_c = scale(outdoor_time, center = TRUE, scale = FALSE)
  )

plot_m4_probe <- probe_interaction(
  model = m4,
  pred = outdoor_time_c,
  modx = envt,
  interval = T
)$interactplot +
  geom_point(data = outdoors_probedat, size = 3)
plot_m4_probe

Centered y-intercept plot

m4_simple_effs <- tibble(
  envt = c('nature', 'urban'),
  modx_group = c('nature', 'urban'),
  int = c(
    coef(m4)[['(Intercept)']], 
    coef(m4)[['(Intercept)']] + coef(m4)[['envturban']]
    ),
  slp = c(
    coef(m4)[['outdoor_time_c']],
    coef(m4)[['outdoor_time_c']] + coef(m4)[['outdoor_time_c:envturban']]
    )
)

plot_m4_probe_intercept <- plot_m4_probe
plot_m4_probe_intercept$layers[[1]] <- NULL

plot_m4_probe_intercept +
  xlim(-7, 7) +
    ylim(-10, 100) +
  geom_abline(
    data = m4_simple_effs, 
    aes(intercept = int, slope = slp, colour = envt, linetype = envt),
    linewidth = 1.5
  ) +
  geom_point(
    data = m4_simple_effs,
    x = 0,
    aes(y = int),
    size = 5,
    shape = 15
  ) +
  geom_vline(xintercept = 0, linetype = 'dotted') +
  labs(
    subtitle = 'outdoor_time mean-centered'
  ) +
  theme(
    legend.position = 'bottom'
  ) +
  NULL

Compare coef estims

As-is

With outdoor_time as-is:

summary(m2)$coefficients |> round(3)
                       Estimate Std. Error t value Pr(>|t|)
(Intercept)              -3.388      4.565  -0.742    0.462
outdoor_time             12.289      0.898  13.682    0.000
envturban                20.290      6.502   3.120    0.003
outdoor_time:envturban   -9.560      1.307  -7.316    0.000

Min-shifted

With outdoor_time min-shifted:

summary(m3)$coefficients |> round(3)
                           Estimate Std. Error t value Pr(>|t|)
(Intercept)                  28.665      2.329  12.307    0.000
outdoor_time_min             12.289      0.898  13.682    0.000
envturban                    -4.645      3.246  -1.431    0.159
outdoor_time_min:envturban   -9.560      1.307  -7.316    0.000

Centered

With outdoor_time mean-centered:

summary(m4)$coefficients |> round(3)
                         Estimate Std. Error t value Pr(>|t|)
(Intercept)                56.451      0.971  58.124        0
outdoor_time_c             12.289      0.898  13.682        0
envturban                 -26.260      1.347 -19.496        0
outdoor_time_c:envturban   -9.560      1.307  -7.316        0

Appendix

LS0tCnRpdGxlOiAiMTEgUGxheWdyb3VuZCDigJMgSW50ZXJhY3Rpb25zIG51bSBjYXQsIGNlbnRlcmluZyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXB9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGludGVyYWN0aW9ucykgICMgZm9yIHByb2JlX2ludGVyYWN0aW9ucygpIHBsb3QKCnNhbGFyeSA8LSByZWFkX2NzdigiLi4vLi4vZGF0YS9zYWxhcnlfbGVjLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpIHw+CiAgbXV0YXRlKAogICAgZW52dCA9IGZhY3RvcihpZmVsc2UoZGVwdCA9PSAxLCAnbmF0dXJlJywgJ3VyYmFuJykpCiAgKQoKIyBtYWtlIHRoZSBhY2N0IHNsb3BlIGEgYml0IHN0ZWVwZXIgdG8gaWxsdXN0cmF0ZSB0aGUgcG9pbnQgYmV0dGVyCiMgYW5kIHJlbmFtZS9yZWxhYmVsIHRoZSBkYXRhIHNvIGl0J3MgYWJvdXQgdGltZSBvdXRkb29ycyBhbmQgbm90IHNhbGFyeQpvdXRkb29ycyA8LSBzYWxhcnkgfD4KICBtdXRhdGUoCiAgICBzYWxhcnlfYXVnID0gaWZlbHNlKAogICAgICBlbnZ0ID09ICduYXR1cmUnLAogICAgICBzYWxhcnkgKyAoc2VydmljZV4yKS8xLjUgLSAxMCwKICAgICAgc2FsYXJ5CiAgICApLAogICAgd2VsbGJlaW5nID0gc2FsYXJ5X2F1ZwogICkgfD4KICByZW5hbWUoCiAgICBvdXRkb29yX3RpbWUgPSBzZXJ2aWNlCiAgKSB8PgogIHNlbGVjdCh3ZWxsYmVpbmcsIGVudnQsIG91dGRvb3JfdGltZSkKYGBgCgoKIyBQbG90IGRhdGEKCmBgYHtyfQpwYWxldHRlX3Byb2JlIDwtIGMoJyM0YWI4ZmMnLCAnI2ZmN2IwMScpCgpvdXRkb29ycyB8PgogIGdncGxvdChhZXMoeCA9IG91dGRvb3JfdGltZSwgeSA9IHdlbGxiZWluZywgY29sb3VyID0gZW52dCkpICsKICBnZW9tX3BvaW50KHNpemUgPSA1KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2UgPSBGKSArCiAgIyBmYWNldF93cmFwKH4gZW52dCkgKwogICMgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGVfcHJvYmUpICsKICBOVUxMCmBgYAoKCgoKYGVudnRgOgoKLSAxID0gdXJiYW4KLSAwID0gbmF0dXJlCgpgYGB7cn0KY29udHJhc3RzKG91dGRvb3JzJGVudnQpCmBgYAoKCmB3ZWxsYmVpbmdgOgoKLSB3ZWxsYmVpbmcgaW4gdGhvc3VhbmRzIG9mIHBvdW5kcwoKCmBvdXRkb29yX3RpbWVgOgoKLSB5ZWFycyBvZiBvdXRkb29yX3RpbWUKCgoKIyBNb2RlbDogbm8gaW50ZXJhY3Rpb24KCmBgYHtyfQptMSA8LSBsbSh3ZWxsYmVpbmcgfiBvdXRkb29yX3RpbWUgKyBlbnZ0LCBkYXRhID0gb3V0ZG9vcnMpCnN1bW1hcnkobTEpCmBgYAoKYGBge3Igd2FybmluZz1GfQpvdXRkb29yc19wcm9iZWRhdCA8LSBvdXRkb29ycyB8PgogIG11dGF0ZSgKICAgIHByZWQgPSBvdXRkb29yX3RpbWUsCiAgICBtb2R4X2dyb3VwID0gZW52dAogICkKICAKcGxvdF9tMV9wcm9iZSA8LSBwcm9iZV9pbnRlcmFjdGlvbigKICBtb2RlbCA9IG0xLAogIHByZWQgPSBvdXRkb29yX3RpbWUsCiAgbW9keCA9IGVudnQsCiAgaW50ZXJ2YWwgPSBUCikkaW50ZXJhY3RwbG90ICsKICBnZW9tX3BvaW50KGRhdGEgPSBvdXRkb29yc19wcm9iZWRhdCwgc2l6ZSA9IDUpCgpwbG90X2RhdGFfcHJvYmUgPC0gcGxvdF9tMV9wcm9iZQoKcGxvdF9kYXRhX3Byb2JlJGxheWVyc1tbMV1dIDwtIE5VTEwKcGxvdF9kYXRhX3Byb2JlJGxheWVyc1tbMV1dIDwtIE5VTEwKYGBgCgpgYGB7cn0KcGxvdF9tMV9wcm9iZQpgYGAKCi0gYmx1ZSBsaW5lOiB0b28gaGlnaCBhdCBiZWdpbm5pbmcsIHRvbyBsb3cgYXQgZW5kCi0gb3JhbmdlIGxpbmU6IHRvbyBsb3cgYXQgYmVnaW5uaW5nLCB0b28gaGlnaCBhdCBlbmQKCm1lZXRpbmcgaW4gdGhlIG1pZGRsZSBtZWFucyB0aGF0IG5laXRoZXIgbGluZSBmaXRzIHRoZSBkYXRhIHZlcnkgd2VsbAoKCgojIyBwbG90IHJhdyBkYXRhCgpgYGB7cn0KcGxvdF9kYXRhX3Byb2JlCmBgYAoKIyMgQ29tcHV0ZSBzaW1wbGUgc2xvcGVzCgpgYGB7cn0KY29lZihtMSkKYGBgCgoKQ29tcHV0ZSBzaW1wbGUgc2xvcGVzLgoKJCQKXGJlZ2lue2FsaWdufQpcd2lkZWhhdHt3ZWxsYmVpbmd9ICY9IFxiZXRhXzAgKyAoXGJldGFfMSBcY2RvdCBvdXRkb29yX3RpbWUpICsgKFxiZXRhXzIgXGNkb3QgZW52dCkgXFwKXHdpZGVoYXR7d2VsbGJlaW5nfSAmPSAxOSAgICArICg3LjggXGNkb3Qgb3V0ZG9vcl90aW1lKSAgICAgKyAoLTI2LjIgXGNkb3QgZW52dCkgXFwgClxlbmR7YWxpZ259CiQkCgpXaGVuICRlbnZ0ID0gMCQgKG5hdHVyZSk6CgokJApcYmVnaW57YWxpZ259Clx3aWRlaGF0e3dlbGxiZWluZ31fe2VudnQ9MH0gJj0gMTkgICAgKyAoNy44IFxjZG90IG91dGRvb3JfdGltZSkgICAgICsgKC0yNi4yIFxjZG90IDApIFxcClx3aWRlaGF0e3dlbGxiZWluZ31fe2VudnQ9MH0gJj0gMTkgICAgKyAoNy44IFxjZG90IG91dGRvb3JfdGltZSkgICAgICBcXApcZW5ke2FsaWdufQokJApXaGVuICRlbnZ0ID0gMSQgKHVyYmFuKToKCiQkClxiZWdpbnthbGlnbn0KXHdpZGVoYXR7d2VsbGJlaW5nfV97ZW52dD0xfSAmPSAxOSAgICArICg3LjggXGNkb3Qgb3V0ZG9vcl90aW1lKSAgICAgKyAoLTI2LjIgXGNkb3QgMSkgXFwKXHdpZGVoYXR7d2VsbGJlaW5nfV97ZW52dD0xfSAmPSAxOSAtIDI2LjIgICArICg3LjggXGNkb3Qgb3V0ZG9vcl90aW1lKSAgICAgIFxcClx3aWRlaGF0e3dlbGxiZWluZ31fe2VudnQ9MX0gJj0gOSAgICsgKDcuOCBcY2RvdCBvdXRkb29yX3RpbWUpICAgICAgXFwKXGVuZHthbGlnbn0KJCQKCgoKIyBNb2RlbDogd2l0aCBpbnRlcmFjdGlvbgoKYGBge3J9Cm0yIDwtIGxtKHdlbGxiZWluZyB+IG91dGRvb3JfdGltZSArIGVudnQgKyBvdXRkb29yX3RpbWU6ZW52dCwgZGF0YSA9IG91dGRvb3JzKQpzdW1tYXJ5KG0yKQpgYGAKCgojIyBDb21wdXRlIHNpbXBsZSBzbG9wZXMKCmBgYHtyfQpjb2VmKG0yKQpgYGAKCgojIyMgQnkgaGFuZAoKJCQKXGJlZ2lue2FsaWdufQpcd2lkZWhhdHt3ZWxsYmVpbmd9ICY9IFxiZXRhXzAgKyAoXGJldGFfMSBcY2RvdCBvdXQpICsgKFxiZXRhXzIgXGNkb3QgZW52dCkgKyAoXGJldGFfMyBcY2RvdCBvdXQgXGNkb3QgZW52dClcXApcd2lkZWhhdHt3ZWxsYmVpbmd9ICY9IC0zLjQgICAgKyAoMTIuMyBcY2RvdCBvdXQpICAgICArICgyMC4zIFxjZG90IGVudnQpICAgICsgKC05LjYgXGNkb3Qgb3V0IFxjZG90IGVudnQpXFwKXGVuZHthbGlnbn0KJCQKCgpXaGVuICRlbnZ0ID0gMCQgKG5hdHVyZSk6CgokJApcYmVnaW57YWxpZ259Clx3aWRlaGF0e3dlbGxiZWluZ31fe2FjY3R9ICY9IC0zLjQgICAgKyAoMTIuMyBcY2RvdCBvdXQpICAgICArICgyMC4zIFxjZG90IGVudnQpICAgICsgKC05LjYgXGNkb3Qgb3V0IFxjZG90IGVudnQpXFwKXHdpZGVoYXR7d2VsbGJlaW5nfV97YWNjdH0gJj0gLTMuNCAgICArICgxMi4zIFxjZG90IG91dCkgICAgICsgKDIwLjMgXGNkb3QgMCkgICAgKyAoLTkuNiBcY2RvdCBvdXQgXGNkb3QgMClcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3thY2N0fSAmPSAtMy40ICAgICsgKDEyLjMgXGNkb3Qgb3V0KVxcClxlbmR7YWxpZ259CiQkCgoKV2hlbiAkZW52dCA9IDEkICh1cmJhbik6CgokJApcYmVnaW57YWxpZ259Clx3aWRlaGF0e3dlbGxiZWluZ31fe21uZ30gJj0gLTMuNCAgICArICgxMi4zIFxjZG90IG91dCkgICAgICsgKDIwLjMgXGNkb3QgZW52dCkgICAgKyAoLTkuNiBcY2RvdCBvdXQgXGNkb3QgZW52dClcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3ttbmd9ICY9IC0zLjQgICAgKyAoMTIuMyBcY2RvdCBvdXQpICAgICArICgyMC4zIFxjZG90IDEpICAgICsgKC05LjYgXGNkb3Qgb3V0IFxjZG90IDEpXFwKXHdpZGVoYXR7d2VsbGJlaW5nfV97bW5nfSAmPSAtMy40ICsgMjAuMyAgICsgKDEyLjMgXGNkb3Qgb3V0KSArICgtOS42IFxjZG90IG91dClcXApcd2lkZWhhdHt3ZWxsYmVpbmd9X3ttbmd9ICY9IDE2LjkgICArICgxMi4zIFxjZG90IG91dCkgKyAoLTkuNiBcY2RvdCBvdXQpXFwKXHdpZGVoYXR7d2VsbGJlaW5nfV97bW5nfSAmPSAxNi45ICAgKyAoKDEyLjMgLSA5LjYpIFxjZG90IG91dCkgXFwKXHdpZGVoYXR7d2VsbGJlaW5nfV97bW5nfSAmPSAxNi45ICAgKyAoMi43IFxjZG90IG91dClcXApcZW5ke2FsaWdufQokJAoKCiMjIyBpbiBSCgpXaGVuIGVudnQgPSAwIChuYXR1cmUpOgoKYGBge3J9CmNvZWYobTIpW1snKEludGVyY2VwdCknXV0gICMgaW50ZXJjZXB0CmNvZWYobTIpW1snb3V0ZG9vcl90aW1lJ11dICAgICAgIyBzbG9wZQpgYGAKCgpXaGVuIGVudnQgPSAxICh1cmJhbikKCmBgYHtyfQpjb2VmKG0yKVtbJyhJbnRlcmNlcHQpJ11dICsgY29lZihtMilbWydlbnZ0dXJiYW4nXV0gICAgICAjIGludGVyY2VwdApjb2VmKG0yKVtbJ291dGRvb3JfdGltZSddXSArIGNvZWYobTIpW1snb3V0ZG9vcl90aW1lOmVudnR1cmJhbiddXSAgIyBzbG9wZQpgYGAKCgoKCiMjIFBsb3QgYXNpcyBtb2RlbAoKYGBge3Igd2FybmluZyA9IEZ9CnBsb3RfbTJfcHJvYmUgPC0gcHJvYmVfaW50ZXJhY3Rpb24oCiAgbW9kZWwgPSBtMiwKICBwcmVkID0gb3V0ZG9vcl90aW1lLAogIG1vZHggPSBlbnZ0LAogIGludGVydmFsID0gVAopJGludGVyYWN0cGxvdCArCiAgZ2VvbV9wb2ludChkYXRhID0gb3V0ZG9vcnNfcHJvYmVkYXQsIHNpemUgPSAzKSArCgogIE5VTEwKCnBsb3RfbTJfcHJvYmUKYGBgCgoKCiMjIyBBc2lzIHktaW50ZXJjZXB0IHBsb3QKCmBgYHtyfQptMl9zaW1wbGVfZWZmcyA8LSB0aWJibGUoCiAgZW52dCA9IGMoJ25hdHVyZScsICd1cmJhbicpLAogIG1vZHhfZ3JvdXAgPSBjKCduYXR1cmUnLCAndXJiYW4nKSwKICBpbnQgPSBjKAogICAgY29lZihtMilbWycoSW50ZXJjZXB0KSddXSwgCiAgICBjb2VmKG0yKVtbJyhJbnRlcmNlcHQpJ11dICsgY29lZihtMilbWydlbnZ0dXJiYW4nXV0KICAgICksCiAgc2xwID0gYygKICAgIGNvZWYobTIpW1snb3V0ZG9vcl90aW1lJ11dLAogICAgY29lZihtMilbWydvdXRkb29yX3RpbWUnXV0gKyBjb2VmKG0yKVtbJ291dGRvb3JfdGltZTplbnZ0dXJiYW4nXV0KICAgICkKKQoKcGxvdF9tMl9wcm9iZV9pbnRlcmNlcHQgPC0gcGxvdF9tMl9wcm9iZQpwbG90X20yX3Byb2JlX2ludGVyY2VwdCRsYXllcnNbWzFdXSA8LSBOVUxMCgpwbG90X20yX3Byb2JlX2ludGVyY2VwdCArCiAgeGxpbSgtNywgNykgKwogIHlsaW0oLTEwLCAxMDApICsKICBnZW9tX2FibGluZSgKICAgIGRhdGEgPSBtMl9zaW1wbGVfZWZmcywgCiAgICBhZXMoaW50ZXJjZXB0ID0gaW50LCBzbG9wZSA9IHNscCwgY29sb3VyID0gZW52dCwgbGluZXR5cGUgPSBlbnZ0KSwKICAgIGxpbmV3aWR0aCA9IDEuNQogICkgKwogIGdlb21fcG9pbnQoCiAgICBkYXRhID0gbTJfc2ltcGxlX2VmZnMsCiAgICB4ID0gMCwKICAgIGFlcyh5ID0gaW50KSwKICAgIHNpemUgPSA1LAogICAgc2hhcGUgPSAxNQogICkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gJ2RvdHRlZCcpICsKICBsYWJzKAogICAgc3VidGl0bGUgPSAnb3V0ZG9vcl90aW1lIGFzLWlzJwogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScKICApICsKICBOVUxMCmBgYAoKCgojIE1pbi1zaGlmdCBwcmVkaWN0b3IKClBsb3QgcHJlZGljdG9yIGFzLWlzLgoKYGBge3J9Cm91dGRvb3JzIHw+CiAgZ2dwbG90KGFlcyh4ID0gb3V0ZG9vcl90aW1lLCB5ID0gd2VsbGJlaW5nKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG91ciA9ICdyZWQnLCBsaW5ld2lkdGggPSAyKSArCiAgZ2VvbV9wb2ludCgpICsKICBOVUxMCmBgYAoKClBsb3QgbWluLXNoaWZ0ZWQgcHJlZGljdG9yLgoKYGBge3J9Cm91dGRvb3JzIDwtIG91dGRvb3JzIHw+CiAgbXV0YXRlKAogICAgb3V0ZG9vcl90aW1lX21pbiA9IG91dGRvb3JfdGltZSAtIG1pbihvdXRkb29yX3RpbWUpCiAgKQoKb3V0ZG9vcnMgfD4KICBnZ3Bsb3QoYWVzKHggPSBvdXRkb29yX3RpbWVfbWluLCB5ID0gd2VsbGJlaW5nKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG91ciA9ICdyZWQnLCBsaW5ld2lkdGggPSAyKSArCiAgZ2VvbV9wb2ludCgpICsKICBOVUxMCmBgYAoKMCBpcyBtZWFuaW5nZnVsIG5vdyA9IHRoZSBtaW5pbXVtIGxlbmd0aCBvZiBvdXRkb29yX3RpbWUuCgoKIyMgTW9kZWwKCmBgYHtyfQptMyA8LSBsbSh3ZWxsYmVpbmcgfiBvdXRkb29yX3RpbWVfbWluICsgZW52dCArIG91dGRvb3JfdGltZV9taW46ZW52dCwgZGF0YSA9IG91dGRvb3JzKQpzdW1tYXJ5KG0zKQpgYGAKCiMjIENvbXB1dGUgc2ltcGxlIHNsb3BlcwoKIyMjIEluIFIKCldoZW4gZW52dCA9IDAgKGFjY3QpOgoKYGBge3J9CmNvZWYobTMpW1snKEludGVyY2VwdCknXV0gICMgaW50ZXJjZXB0CmNvZWYobTMpW1snb3V0ZG9vcl90aW1lX21pbiddXSAgICAgICMgc2xvcGUKYGBgCgoKV2hlbiBlbnZ0ID0gMSAobW5nKQoKYGBge3J9CmNvZWYobTMpW1snKEludGVyY2VwdCknXV0gKyBjb2VmKG0zKVtbJ2VudnR1cmJhbiddXSAgICAgICMgaW50ZXJjZXB0CmNvZWYobTMpW1snb3V0ZG9vcl90aW1lX21pbiddXSArIGNvZWYobTMpW1snb3V0ZG9vcl90aW1lX21pbjplbnZ0dXJiYW4nXV0gICMgc2xvcGUKYGBgCgoKCgojIyBQbG90IHNoaWZ0ZWQgbW9kZWwKCmBgYHtyIHdhcm5pbmc9Rn0Kb3V0ZG9vcnNfcHJvYmVkYXQgPC0gb3V0ZG9vcnNfcHJvYmVkYXQgfD4KICBtdXRhdGUoCiAgICBvdXRkb29yX3RpbWVfbWluID0gb3V0ZG9vcl90aW1lIC0gbWluKG91dGRvb3JfdGltZSkKICApCgpwbG90X20zX3Byb2JlIDwtIHByb2JlX2ludGVyYWN0aW9uKAogIG1vZGVsID0gbTMsCiAgcHJlZCA9IG91dGRvb3JfdGltZV9taW4sCiAgbW9keCA9IGVudnQsCiAgaW50ZXJ2YWwgPSBUCikkaW50ZXJhY3RwbG90ICsKICBnZW9tX3BvaW50KGRhdGEgPSBvdXRkb29yc19wcm9iZWRhdCwgc2l6ZSA9IDMpCgpwbG90X20zX3Byb2JlCmBgYAoKIyMjIFNoaWZ0ZWQgeS1pbnRlcmNlcHQgcGxvdAoKYGBge3J9Cm0zX3NpbXBsZV9lZmZzIDwtIHRpYmJsZSgKICBlbnZ0ID0gYygnbmF0dXJlJywgJ3VyYmFuJyksCiAgbW9keF9ncm91cCA9IGMoJ25hdHVyZScsICd1cmJhbicpLAogIGludCA9IGMoCiAgICBjb2VmKG0zKVtbJyhJbnRlcmNlcHQpJ11dLCAKICAgIGNvZWYobTMpW1snKEludGVyY2VwdCknXV0gKyBjb2VmKG0zKVtbJ2VudnR1cmJhbiddXQogICAgKSwKICBzbHAgPSBjKAogICAgY29lZihtMylbWydvdXRkb29yX3RpbWVfbWluJ11dLAogICAgY29lZihtMylbWydvdXRkb29yX3RpbWVfbWluJ11dICsgY29lZihtMylbWydvdXRkb29yX3RpbWVfbWluOmVudnR1cmJhbiddXQogICAgKQopCgpwbG90X20zX3Byb2JlX2ludGVyY2VwdCA8LSBwbG90X20zX3Byb2JlCnBsb3RfbTNfcHJvYmVfaW50ZXJjZXB0JGxheWVyc1tbMV1dIDwtIE5VTEwKCnBsb3RfbTNfcHJvYmVfaW50ZXJjZXB0ICsKICB4bGltKC03LCA3KSArCiAgICB5bGltKC0xMCwgMTAwKSArCiAgZ2VvbV9hYmxpbmUoCiAgICBkYXRhID0gbTNfc2ltcGxlX2VmZnMsIAogICAgYWVzKGludGVyY2VwdCA9IGludCwgc2xvcGUgPSBzbHAsIGNvbG91ciA9IGVudnQsIGxpbmV0eXBlID0gZW52dCksCiAgICBsaW5ld2lkdGggPSAxLjUKICApICsKICBnZW9tX3BvaW50KAogICAgZGF0YSA9IG0zX3NpbXBsZV9lZmZzLAogICAgeCA9IDAsCiAgICBhZXMoeSA9IGludCksCiAgICBzaXplID0gNSwKICAgIHNoYXBlID0gMTUKICApICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICdkb3R0ZWQnKSArCiAgbGFicygKICAgIHN1YnRpdGxlID0gJ291dGRvb3JfdGltZSBtaW4tc2hpZnRlZCcKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nCiAgKSArCiAgTlVMTApgYGAKCgoKCiMgQ2VudHJlIHByZWRpY3RvcgoKUGxvdCBjZW50ZXJlZCBwcmVkaWN0b3IuCgpgYGB7cn0Kb3V0ZG9vcnMgPC0gb3V0ZG9vcnMgfD4KICBtdXRhdGUoCiAgICBvdXRkb29yX3RpbWVfYyA9IHNjYWxlKG91dGRvb3JfdGltZSwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSkKICApCgpvdXRkb29ycyB8PgogIGdncGxvdChhZXMoeCA9IG91dGRvb3JfdGltZV9jLCB5ID0gd2VsbGJlaW5nKSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGNvbG91ciA9ICdyZWQnLCBsaW5ld2lkdGggPSAyKSArCiAgZ2VvbV9wb2ludCgpICsKICBOVUxMCmBgYAoKYGBge3J9CnJvdW5kKG1lYW4ob3V0ZG9vcnMkb3V0ZG9vcl90aW1lX2MpKQpgYGAKCgojIyBNb2RlbAoKYGBge3J9Cm00IDwtIGxtKHdlbGxiZWluZyB+IG91dGRvb3JfdGltZV9jICsgZW52dCArIG91dGRvb3JfdGltZV9jOmVudnQsIGRhdGEgPSBvdXRkb29ycykKc3VtbWFyeShtNCkKYGBgCgoKIyMgUGxvdCBjZW50ZXJlZCBtb2RlbAoKYGBge3Igd2FybmluZyA9IEZ9Cm91dGRvb3JzX3Byb2JlZGF0IDwtIG91dGRvb3JzX3Byb2JlZGF0IHw+CiAgbXV0YXRlKAogICAgb3V0ZG9vcl90aW1lX2MgPSBzY2FsZShvdXRkb29yX3RpbWUsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpCiAgKQoKcGxvdF9tNF9wcm9iZSA8LSBwcm9iZV9pbnRlcmFjdGlvbigKICBtb2RlbCA9IG00LAogIHByZWQgPSBvdXRkb29yX3RpbWVfYywKICBtb2R4ID0gZW52dCwKICBpbnRlcnZhbCA9IFQKKSRpbnRlcmFjdHBsb3QgKwogIGdlb21fcG9pbnQoZGF0YSA9IG91dGRvb3JzX3Byb2JlZGF0LCBzaXplID0gMykKcGxvdF9tNF9wcm9iZQpgYGAKCgojIyMgQ2VudGVyZWQgeS1pbnRlcmNlcHQgcGxvdAoKYGBge3J9Cm00X3NpbXBsZV9lZmZzIDwtIHRpYmJsZSgKICBlbnZ0ID0gYygnbmF0dXJlJywgJ3VyYmFuJyksCiAgbW9keF9ncm91cCA9IGMoJ25hdHVyZScsICd1cmJhbicpLAogIGludCA9IGMoCiAgICBjb2VmKG00KVtbJyhJbnRlcmNlcHQpJ11dLCAKICAgIGNvZWYobTQpW1snKEludGVyY2VwdCknXV0gKyBjb2VmKG00KVtbJ2VudnR1cmJhbiddXQogICAgKSwKICBzbHAgPSBjKAogICAgY29lZihtNClbWydvdXRkb29yX3RpbWVfYyddXSwKICAgIGNvZWYobTQpW1snb3V0ZG9vcl90aW1lX2MnXV0gKyBjb2VmKG00KVtbJ291dGRvb3JfdGltZV9jOmVudnR1cmJhbiddXQogICAgKQopCgpwbG90X200X3Byb2JlX2ludGVyY2VwdCA8LSBwbG90X200X3Byb2JlCnBsb3RfbTRfcHJvYmVfaW50ZXJjZXB0JGxheWVyc1tbMV1dIDwtIE5VTEwKCnBsb3RfbTRfcHJvYmVfaW50ZXJjZXB0ICsKICB4bGltKC03LCA3KSArCiAgICB5bGltKC0xMCwgMTAwKSArCiAgZ2VvbV9hYmxpbmUoCiAgICBkYXRhID0gbTRfc2ltcGxlX2VmZnMsIAogICAgYWVzKGludGVyY2VwdCA9IGludCwgc2xvcGUgPSBzbHAsIGNvbG91ciA9IGVudnQsIGxpbmV0eXBlID0gZW52dCksCiAgICBsaW5ld2lkdGggPSAxLjUKICApICsKICBnZW9tX3BvaW50KAogICAgZGF0YSA9IG00X3NpbXBsZV9lZmZzLAogICAgeCA9IDAsCiAgICBhZXMoeSA9IGludCksCiAgICBzaXplID0gNSwKICAgIHNoYXBlID0gMTUKICApICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICdkb3R0ZWQnKSArCiAgbGFicygKICAgIHN1YnRpdGxlID0gJ291dGRvb3JfdGltZSBtZWFuLWNlbnRlcmVkJwogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gJ2JvdHRvbScKICApICsKICBOVUxMCmBgYAoKCiMgQ29tcGFyZSBjb2VmIGVzdGltcwoKIyMgQXMtaXMKCldpdGggYG91dGRvb3JfdGltZWAgYXMtaXM6CgpgYGB7cn0Kc3VtbWFyeShtMikkY29lZmZpY2llbnRzIHw+IHJvdW5kKDMpCmBgYAoKCiMjIE1pbi1zaGlmdGVkCgpXaXRoIGBvdXRkb29yX3RpbWVgIG1pbi1zaGlmdGVkOgoKYGBge3J9CnN1bW1hcnkobTMpJGNvZWZmaWNpZW50cyB8PiByb3VuZCgzKQpgYGAKCgojIyBDZW50ZXJlZAoKV2l0aCBgb3V0ZG9vcl90aW1lYCBtZWFuLWNlbnRlcmVkOgoKYGBge3J9CnN1bW1hcnkobTQpJGNvZWZmaWNpZW50cyB8PiByb3VuZCgzKQpgYGAKCgojIEFwcGVuZGl4CgotIGFsbCBzaW1wbGUgc2xvcGUgY2FsY3VsYXRpb25zIGZvciBhbGwgbW9kZWxzCgoKCgo=